from docx import Document
import copy
import json
from datetime import datetime
import os
from pathlib import Path
from docx.shared import Cm
import random


def get_workdays_in_months(months, holidays=None, workdays=None):
    """生成指定月份的工作日，按周分组（ISO周，周一开始），返回JSON格式字符串

    规则：
    1. 以周一为一周的开始
    2. 如果某周的周一在当月，则该周属于当月
    3. 排除节假日
    4. 包含调休工作日（原本是周末但需要上班的日期）

    参数：
    - months: 月份列表，如 ["2025-10", "2025-11"]
    - holidays: 节假日列表，如 ["2025-10-01", "2025-10-02"]，为None时使用默认
    - workdays: 调休工作日列表，如 ["2025-01-26", "2025-02-08"]（原本是周末但要上班），为None时使用默认
    """
    from datetime import datetime, timedelta
    import json

    # 默认节假日（2025年）
    if holidays is None:
        holidays = [
            # 元旦（1月1日）
            "2025-01-01",
            # 春节（1月28日-2月4日，共8天）
            "2025-01-28", "2025-01-29", "2025-01-30", "2025-01-31",
            "2025-02-01", "2025-02-02", "2025-02-03", "2025-02-04",
            # 清明节（4月4日-6日）
            "2025-04-04", "2025-04-05", "2025-04-06",
            # 劳动节（5月1日-5日）
            "2025-05-01", "2025-05-02", "2025-05-03", "2025-05-04", "2025-05-05",
            # 端午节（5月31日-6月2日）
            "2025-05-31", "2025-06-01", "2025-06-02",
            # 中秋节（10月6日-8日）
            "2025-10-06", "2025-10-07", "2025-10-08",
            # 国庆节（10月1日-8日）
            "2025-10-01", "2025-10-02", "2025-10-03", "2025-10-04",
            "2025-10-05", "2025-10-06", "2025-10-07", "2025-10-08",
        ]

    # 默认调休工作日（2025年）
    if workdays is None:
        workdays = [
            # 春节调休：1月26日（周日）、2月8日（周六）上班
            "2025-01-26", "2025-02-08",
            # 劳动节调休：4月27日（周日）上班
            "2025-04-27",
            # 国庆节调休：9月28日（周日）、10月11日（周六）上班
            "2025-09-28", "2025-10-11",
        ]

    # 转换为集合，提高查询效率
    holiday_set = set(holidays)
    workday_set = set(workdays)

    weekly_workdays = {}

    for month in months:
        year, month_num = map(int, month.split("-"))
        first_day = datetime(year, month_num, 1)

        # 计算当月最后一天
        next_month_num = 1 if month_num == 12 else month_num + 1
        next_year = year + 1 if month_num == 12 else year
        last_day = datetime(next_year, next_month_num, 1) - timedelta(days=1)

        # 找到当月所有"周一"
        current_day = first_day
        week_mondays = []

        # 找到第一个周一
        while current_day <= last_day:
            if current_day.weekday() == 0:  # 周一
                week_mondays.append(current_day)
                current_day += timedelta(days=7)
            else:
                current_day += timedelta(days=1)

        # 处理月初不是周一的情况（第0周）
        if first_day.weekday() != 0:
            first_monday_before = first_day - timedelta(days=first_day.weekday())
            week_mondays.insert(0, first_monday_before)

        # 遍历当月所有日期，分配到对应的周
        current_day = first_day
        while current_day <= last_day:
            date_str = current_day.strftime("%Y-%m-%d")

            # ⚡ 判断逻辑：
            # 1. 如果在调休工作日列表中 → 是工作日
            # 2. 如果是节假日 → 不是工作日
            # 3. 如果是周末（周六日）→ 不是工作日
            # 4. 其他情况（周一到周五且非节假日）→ 是工作日

            is_weekend = current_day.weekday() >= 5  # 周六、周日
            is_holiday = date_str in holiday_set
            is_makeup_workday = date_str in workday_set  # 调休上班日

            # 判断是否为工作日
            is_workday = is_makeup_workday or (not is_weekend and not is_holiday)

            if is_workday:
                # 找到当前日期属于哪个周
                week_number = 0
                for idx, monday in enumerate(week_mondays):
                    if current_day >= monday:
                        week_number = idx + 1
                    else:
                        break

                if week_number == 0:
                    week_number = 1

                # 生成周键
                week_key = f"{year}年{month_num}月第{week_number}周"

                if week_key not in weekly_workdays:
                    weekly_workdays[week_key] = []
                weekly_workdays[week_key].append(date_str)

            current_day += timedelta(days=1)

    return json.dumps(weekly_workdays, ensure_ascii=False, indent=4)




def 生成文档复制件(data):
    """复制模板生成工单（彻底消除空白页）"""
    from docx.enum.text import WD_BREAK
    from docx.shared import Pt

    template_path = "问题处理记录.docx"
    template_path_obj = Path(template_path)
    if not template_path_obj.exists():
        print(f"错误：原始模板文件 '{template_path}' 不存在，请检查路径！")
        return

    os.makedirs("工单生成", exist_ok=True)

    for week_key, date_list in data.items():
        doc = Document(template_path)

        # ---------------------- 1. 提取模板元素 ----------------------
        title_para = None
        for para in doc.paragraphs:
            if para.text.strip() == "问题处理记录表":
                title_para = para
                break

        if not doc.tables:
            print(f"警告：周 '{week_key}' 模板中未找到表格，跳过生成！")
            continue
        original_table = doc.tables[0]

        # ---------------------- 2. 重新构建文档 ----------------------
        doc.element.body.clear()
        total_tables = len(date_list) * 2
        print(f"📋 周 '{week_key}' 需生成 {total_tables} 个表格")

        for i in range(total_tables):
            # 2.1 添加标题
            if title_para:
                new_title_xml = copy.deepcopy(title_para._element)
                doc.element.body.append(new_title_xml)

            # 2.2 添加表格
            new_table_xml = copy.deepcopy(original_table._element)
            doc.element.body.append(new_table_xml)

            # 2.3 ⚡ 关键优化：分页符直接插入表格最后一个单元格
            if i != total_tables - 1:
                # 获取刚添加的表格对象
                current_table = doc.tables[-1]
                last_row = current_table.rows[-1]
                last_cell = last_row.cells[-1]

                # 在单元格内添加分页符（避免创建独立段落）
                if last_cell.paragraphs:
                    last_para = last_cell.paragraphs[-1]
                else:
                    last_para = last_cell.add_paragraph()

                run = last_para.add_run()
                run.add_break(WD_BREAK.PAGE)

                # 设置段落格式为0间距（消除额外空白）
                last_para.paragraph_format.space_before = Pt(0)
                last_para.paragraph_format.space_after = Pt(0)

        # ---------------------- 3. 深度清理末尾元素 ----------------------
        def deep_clean_blank_pages(doc):
            """彻底清理空白页：删除末尾所有非表格元素"""
            body = doc.element.body
            last_table_idx = -1

            # 找到最后一个表格的索引
            for idx, element in enumerate(body):
                if element.tag.endswith('tbl'):  # 表格元素的标签
                    last_table_idx = idx

            # 删除最后表格之后的所有元素
            if last_table_idx != -1:
                elements_to_remove = list(body)[last_table_idx + 1:]
                for element in elements_to_remove:
                    body.remove(element)

                if elements_to_remove:
                    print(f"🗑️  周 '{week_key}' 清理末尾 {len(elements_to_remove)} 个冗余元素")

        deep_clean_blank_pages(doc)

        # ---------------------- 4. 验证与保存 ----------------------
        generated_tables = len(doc.tables)
        has_title = any(para.text.strip() == "问题处理记录表" for para in doc.paragraphs)

        print(f"📄 周 '{week_key}' 生成 {generated_tables} 个表格，"
              f"{'✅' if has_title else '❌'} 标题保留")

        save_path = f'工单生成/{week_key}.docx'
        try:
            doc.save(save_path)
            print(f"✅ 周 '{week_key}' 工单生成完成：{save_path}\n")
        except Exception as e:
            print(f"❌ 周 '{week_key}' 保存失败：{e}\n")


def 获取生成后的文件数量():
    """获取工单生成文件夹下的所有docx文件"""
    current_script_dir = os.path.dirname(os.path.abspath(__file__))
    docx_dir = Path(current_script_dir) / "工单生成"
    return [file for file in docx_dir.iterdir() if file.is_file() and file.suffix == ".docx"]


def 填写工单(files, **workdays_dict):
    """填充工单内容：替换日期、问题/结果、插入图片，每2个表格日期向后推1天（禁止跨月顺延）"""
    # 基础配置不变，只修改日期顺延逻辑
    IMG_WIDTH = Cm(3.6)
    IMG_HEIGHT = Cm(1.8)
    DATA_GROUPS = [
        {"problem": "无法登陆呼叫器", "result": "重启即可"},
        {"problem": "无法叫号", "result": "网络问题，已联系网络同事查看并处理"},
        {"problem": "呼叫器不显示照片", "result": "重新启动呼叫器之后正常显示"},
        {"problem": "需要协助收件", "result": "已现场协助"},
        {"problem": "收件时提示表单异常", "result": "已现场协助"},
        {"problem": "无法签章", "result": "选择配置了签章编码的窗口即可"},
        {"problem": "无法打开系统", "result": "网络异常，已联系网络运维查看"},
        {"problem": "叫号没声音", "result": "取号机功放问题"},
        {"problem": "表单无法校验信息", "result": "基本信息填写错误"},
        {"problem": "通知书打印失败", "result": "打印机异常，已联系硬件运维"},
        {"problem": "需要协助查看办事指南", "result": "已现场协助"},
        {"problem": "无法打开业务受理", "result": "登录超时，刷新即可"},
        {"problem": "排队号无法获取取号信息", "result": "取号信息待解密，解密完成即可"},
        {"problem": "收件无法上传材料", "result": "压缩后正常上传"},
        {"problem": "需要协助填写表单", "result": "已现场协助"},
        {"problem": "一件事收件表单咨询", "result": "已现场解答"},
        {"problem": "一件事收件需要协助", "result": "已现场协助"},
        {"problem": "咨询通办收件", "result": "已现场协助"},
        {"problem": "一件事收件需要协助", "result": "已现场协助"},
        {"problem": "咨询一件事推送情况", "result": "已查询并截图送达"},
        {"problem": "咨询办理结果", "result": "已指导业务查询查看结果"},
        {"problem": "出件结果材料咨询", "result": "已解答"},
        {"problem": "呼叫器无法登录", "result": "重启呼叫器后正常登录"},
    ]
    IMGS = [{"img": "img/符迁迁.png"},
            {"img": "img/段怡.png"},
            {"img": "img/张洪梅.png"},
            {"img": "img/勾八.png"},
            {"img": "img/周轩兆.png"},
            {"img": "img/周泽宇.png"},
            {"img": "img/宋丹.png"},
            {"img": "img/陈辰.png"},
            {"img": "img/黄永恒.png"},
            {"img": "img/付娜娜.png"},


            ]

    # 校验图片文件夹和文件是否存在
    img_dir = Path("img")
    if not img_dir.exists():
        print("警告：img图片文件夹不存在，图片插入会失败！")
    else:
        for img_info in IMGS:
            if not (img_dir / img_info["img"]).exists():
                print(f"警告：图片文件 '{img_info['img']}' 不存在！")

    os.makedirs("工单填写", exist_ok=True)

    for week_key, date_list in workdays_dict.items():
        doc_path = f'工单生成/{week_key}.docx'
        if not Path(doc_path).exists():
            print(f"❌ 跳过周 '{week_key}'：未找到对应的基础工单文件")
            continue

        # 提取当前周日期的月份（确保不跨月）
        current_month = datetime.strptime(date_list[0], "%Y-%m-%d").month
        print(f"\n📝 开始填充周 '{week_key}' 工单（日期列表：{date_list}，所属月份：{current_month}月）")
        doc = Document(doc_path)
        table_counter = 0  # 表格计数器：每2个表格切换日期
        date_index = 0     # 当前使用的日期索引

        # 遍历所有表格填充内容
        for table in doc.tables:
            # 关键修复：如果日期索引已用尽，直接使用最后一个日期（不跨月顺延）
            if date_index >= len(date_list):
                current_date = datetime.strptime(date_list[-1], "%Y-%m-%d")
            else:
                current_date = datetime.strptime(date_list[date_index], "%Y-%m-%d")

            table_counter += 1
            selected = random.choice(DATA_GROUPS)
            img_info = random.choice(IMGS)
            img_path = img_info["img"]

            # 遍历表格行和单元格（填充逻辑不变）
            for row in table.rows:
                for cell in row.cells:
                    image_processed = False
                    for para in cell.paragraphs:
                        if "日期" in para.text:
                            for run in para.runs:
                                if "日期" in run.text:
                                    run.text = run.text.replace("日期", current_date.strftime("%Y-%m-%d"))
                        if "112" in para.text:
                            for run in para.runs:
                                if "112" in run.text:
                                    run.text = run.text.replace("112", selected["problem"])
                        if "333" in para.text:
                            for run in para.runs:
                                if "333" in run.text:
                                    run.text = run.text.replace("333", selected["result"])
                        if "img-place" in para.text and not image_processed:
                            para.clear()
                            run = para.add_run()
                            try:
                                run.add_picture(img_path, width=IMG_WIDTH, height=IMG_HEIGHT)
                            except Exception as e:
                                print(f"❌ 插入图片失败：{e}")
                            image_processed = True

            # 每处理2个表格，日期索引+1（不跨月）
            if table_counter % 2 == 0:
                if date_index + 1 < len(date_list):
                    date_index += 1
                    next_date = datetime.strptime(date_list[date_index], "%Y-%m-%d")
                    print(f"📅 已处理{table_counter}个表格，日期更新至：{next_date.strftime('%Y-%m-%d')}")
                else:
                    # 日期索引用尽，不再顺延，保持最后一个日期
                    print(f"📅 已处理{table_counter}个表格，已达当月最后一个工作日，日期保持：{current_date.strftime('%Y-%m-%d')}")

        # 保存填充后的工单
        save_path = f'工单填写/{week_key}.docx'
        try:
            doc.save(save_path)
            print(f"✅ 周 '{week_key}' 工单填充完成，保存至：{save_path}")
        except Exception as e:
            print(f"❌ 保存工单失败：{e}")


if __name__ == '__main__':
    # 配置目标月份（可修改为多个，如["2025-10", "2025-11"]）
    target_months = ["2025-11"]

    # 1. 生成每月工作日（按周分组）
    print("第一步：生成目标月份工作日列表...")
    workdays_json = get_workdays_in_months(target_months)
    workdays_dict = json.loads(workdays_json)
    print(workdays_dict)
    print(f"📊 生成完成，共 {len(workdays_dict)} 周工作日：\n{workdays_json}")

    # 2. 生成基础工单（复制原始模板）
    print("\n第二步：生成基础工单文件...")
    生成文档复制件(workdays_dict)

    # 3. 获取生成的基础工单文件
    files = 获取生成后的文件数量()
    print(f"\n第三步：找到 {len(files)} 个基础工单文件")

    # 4. 填充工单内容
    print("\n第四步：开始填充工单数据...")
    填写工单(files, **workdays_dict)

    print("\n🎉 所有工单处理完成！")